"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.minifyTemplate = exports.createMinifier = void 0;
const ts = require("typescript");
const ts_is_kind_1 = require("./ts-is-kind");
function isSymbol(ch) {
    return ch == ';' || ch == ':' || ch == '{' || ch == '}' || ch == ',';
}
function isSpace(ch) {
    return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
}
const stateMachine = {
    ';': {
        next(ch) {
            if (ch == '(')
                return { state: ['(', { count: 1 }] };
            if (ch == '\'' || ch == '"')
                return { state: ch };
            if (isSpace(ch))
                return { skipEmit: true };
            if (ch == '/')
                return { state: ';/', skipEmit: true };
            if (isSymbol(ch))
                return;
            return { state: 'x' };
        },
        flush() {
            return { state: ';$' };
        }
    },
    ';$': {
        next(ch) {
            if (ch == '(')
                return { state: ['(', { count: 1 }] };
            if (ch == '\'' || ch == '"')
                return { state: ch };
            if (isSpace(ch))
                return { skipEmit: true, state: ' ' }; // we may need a space
            if (ch == '/')
                return { state: '/', skipEmit: true };
            if (isSymbol(ch))
                return { state: ';' };
            return { state: 'x' };
        }
    },
    'x': {
        next(ch) {
            if (ch == '(')
                return { state: ['(', { count: 1 }] };
            if (ch == '\'' || ch == '"')
                return { state: ch };
            if (isSpace(ch))
                return { state: ' ', skipEmit: true };
            if (ch == '/')
                return { state: '/', skipEmit: true };
            if (isSymbol(ch))
                return { state: ';' };
        }
    },
    ' ': {
        next(ch) {
            if (ch == '(')
                return { state: ['(', { count: 1 }], emit: ' ' + ch };
            if (ch == '\'' || ch == '"')
                return { state: ch, emit: ' ' + ch };
            if (isSpace(ch))
                return { state: ' ', skipEmit: true };
            if (ch == '/')
                return { state: '/', skipEmit: true };
            if (isSymbol(ch))
                return { state: ';' };
            return { state: 'x', emit: ' ' + ch };
        },
        flush(last) {
            if (!last)
                return { emit: ' ', state: ';$' };
        }
    },
    '\n': {
        next(ch) {
            if (ch == '(')
                return { state: ['(', { count: 1 }], emit: '\n' + ch };
            if (ch == '\'' || ch == '"')
                return { state: ch, emit: '\n' + ch };
            if (isSpace(ch))
                return { state: '\n', skipEmit: true };
            if (ch == '/')
                return { state: '/', emit: '\n' };
            if (isSymbol(ch))
                return { state: ';', emit: '\n' + ch };
            return { state: 'x', emit: '\n' + ch };
        }
    },
    "'": {
        next(ch) {
            if (ch == '\'')
                return { state: ';' };
        }
    },
    '"': {
        next(ch) {
            if (ch == '"')
                return { state: ';' };
        }
    },
    '(': {
        next(ch, { count }) {
            if (ch == '(')
                return { state: ['(', { count: count + 1 }] };
            if (ch == ')')
                if (count > 1)
                    return { state: ['(', { count: count - 1 }] };
                else
                    return { state: ';' }; // maybe return ' '? then it'd always add space after
        }
    },
    '/': {
        next(ch) {
            if (ch == '/')
                return { state: '//', skipEmit: true };
            if (ch == '*')
                return { state: '/*', skipEmit: true };
            return { state: ';', emit: '/' + ch };
        },
        flush() {
            return { state: '/$', emit: '/' };
        }
    },
    '//': {
        next(ch) {
            if (ch == '\n')
                return { state: ' ', skipEmit: true };
            return { skipEmit: true };
        },
        flush(last) {
            if (last)
                return { skipEmit: true };
            return { state: '//$', emit: '//' };
        }
    },
    ';/': {
        next(ch) {
            if (ch == '/')
                return { state: ';//', skipEmit: true };
            if (ch == '*')
                return { state: ';/*', skipEmit: true };
            return { state: ';', emit: '/' + ch };
        },
        flush() {
            return { state: '/$', emit: '/' };
        }
    },
    ';//': {
        next(ch) {
            if (ch == '\n')
                return { state: ';', skipEmit: true };
            return { skipEmit: true };
        },
        flush(last) {
            if (last)
                return { skipEmit: true };
            return { state: '//$', emit: '//' };
        }
    },
    '/$': {},
    '//$': {
        next(ch) {
            if (ch == '\n')
                return { state: '\n', skipEmit: true };
            return { skipEmit: true };
        }
    },
    '/*': {
        next(ch) {
            if (ch == '*')
                return { state: '/**', skipEmit: true };
            return { skipEmit: true };
        },
        flush(last) {
            if (last)
                return { skipEmit: true };
            return { state: '/*$', emit: '/*' };
        }
    },
    '/**': {
        next(ch) {
            if (ch == '/')
                return { state: ' ', skipEmit: true };
            return { state: '/*', skipEmit: true };
        }
    },
    ';/*': {
        next(ch) {
            if (ch == '*')
                return { state: ';/**', skipEmit: true };
            return { skipEmit: true };
        },
        flush(last) {
            if (last)
                return { skipEmit: true };
            return { state: '/*$', emit: '/*' };
        }
    },
    ';/**': {
        next(ch) {
            if (ch == '/')
                return { state: ';', skipEmit: true };
            return { state: ';/*', skipEmit: true };
        }
    },
    '/*$': {
        next(ch) {
            if (ch == '*')
                return { state: '/*$*', skipEmit: true };
            return { skipEmit: true };
        }
    },
    '/*$*': {
        next(ch) {
            if (ch == '/')
                return { state: ';', emit: '*/' };
            return { state: '/*$', skipEmit: true };
        }
    }
};
function createMinifier() {
    let state = ';';
    let stateData = undefined;
    return (next, last = false) => {
        let minified = '';
        function apply(result, ch) {
            if (!result) {
                if (ch !== undefined)
                    minified += ch;
            }
            else {
                if (result.state !== undefined) {
                    if (typeof result.state === 'string')
                        state = result.state;
                    else {
                        state = result.state[0];
                        stateData = result.state[1];
                    }
                }
                if (result.emit !== undefined)
                    minified += result.emit;
                else if (result.skipEmit !== true && ch !== undefined)
                    minified += ch;
            }
        }
        let pos = 0;
        let len = next.length;
        while (pos < len) {
            const ch = next[pos++];
            const reducer = stateMachine[state];
            const prevState = state;
            const reducerResult = reducer.next && reducer.next(ch, stateData);
            apply(reducerResult, ch);
            // console.log('next(', { ch, state: prevState }, '): ', reducerResult, ' -> ', { state, minified });
        }
        const reducer = stateMachine[state];
        const prevState = state;
        const reducerResult = reducer.flush && reducer.flush(last);
        apply(reducerResult);
        // console.log('flush', { state: prevState }, '): ', reducerResult, ' -> ', { state, minified });
        return minified;
    };
}
exports.createMinifier = createMinifier;
function minifyTemplate(templateLiteral, factory) {
    const minifier = createMinifier();
    if (ts_is_kind_1.isNoSubstitutionTemplateLiteral(templateLiteral)) {
        const node = factory.createNoSubstitutionTemplateLiteral(minifier(templateLiteral.text, true));
        return node;
    }
    else if (ts_is_kind_1.isTemplateExpression(templateLiteral)) {
        const head = factory.createTemplateHead(minifier(templateLiteral.head.text));
        const templateSpans = templateLiteral.templateSpans.map(span => factory.createTemplateSpan(span.expression, span.literal.kind === ts.SyntaxKind.TemplateMiddle
            ? factory.createTemplateMiddle(minifier(span.literal.text))
            : factory.createTemplateTail(minifier(span.literal.text, true))));
        const node = factory.createTemplateExpression(head, templateSpans);
        return node;
    }
    return templateLiteral;
}
exports.minifyTemplate = minifyTemplate;
